home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _201958F683104B718D8AA39EC183F072 < prev    next >
Encoding:
Text File  |  2002-06-17  |  37.7 KB  |  1,747 lines

  1. // Copyright (C) 2001-2002 Raven Software.
  2. //
  3. // BG_Player.c
  4.  
  5. #include "q_shared.h"
  6. #include "bg_public.h"
  7. #include "bg_local.h"
  8.  
  9. #include "..\ghoul2\g2.h"
  10.  
  11. #include "../cgame/animtable.h"
  12.  
  13. #ifdef QAGAME
  14. #include "g_local.h"
  15. #endif
  16.  
  17. #ifdef UI_EXPORTS
  18. #include "../ui/ui_local.h"
  19. #endif
  20.  
  21. #ifndef UI_EXPORTS
  22. #ifndef QAGAME
  23. #include "../cgame/cg_local.h"
  24. #endif
  25. #endif
  26.  
  27. TCharacterTemplate        *bg_characterTemplates = NULL;
  28. TItemTemplate            *bg_itemTemplates       = NULL;
  29. int                        bg_identityCount       = 0;
  30. TIdentity                bg_identities[MAX_IDENTITIES];
  31.  
  32. goutfitting_t            bg_outfittings[MAX_OUTFITTINGS];
  33. int                        bg_outfittingCount     = 0;
  34.  
  35. char                    bg_availableOutfitting[WP_NUM_WEAPONS] = {-1};
  36.  
  37. int bg_outfittingGroups[OUTFITTING_GROUP_MAX][MAX_OUTFITTING_GROUPITEM] = 
  38. {
  39.     { MODELINDEX_WEAPON_AK74,        MODELINDEX_WEAPON_M4,        MODELINDEX_WEAPON_USAS12, MODELINDEX_WEAPON_MSG90A1,    MODELINDEX_WEAPON_M60,    MODELINDEX_WEAPON_MP5,    MODELINDEX_WEAPON_RPG7,     MODELINDEX_WEAPON_MM1, -1, -1 },
  40.     { MODELINDEX_WEAPON_M590,        MODELINDEX_WEAPON_MICROUZI,    MODELINDEX_WEAPON_M3A1,        -1, -1, -1, -1, -1, -1, - 1 },
  41.     { MODELINDEX_WEAPON_M19,        MODELINDEX_WEAPON_SOCOM,    -1,                            -1,                            -1, -1, -1, -1, -1, -1 },
  42.     { MODELINDEX_WEAPON_SMOHG92,    MODELINDEX_WEAPON_M84,        MODELINDEX_WEAPON_M15,        MODELINDEX_WEAPON_ANM14,    -1, -1, -1, -1, -1, -1 },
  43.     { MODELINDEX_ARMOR,                MODELINDEX_NIGHTVISION,        MODELINDEX_THERMAL,            -1,                            -1, -1, -1, -1, -1, -1 },
  44. };
  45.  
  46. /*
  47. ===================
  48. PM_StartLegsAnim
  49.  
  50. Starts a new leg animation for the given playerstate
  51. ===================
  52. */
  53. static void PM_StartLegsAnim( playerState_t* ps, int anim ) 
  54. {
  55.     if ( ps->pm_type >= PM_DEAD ) 
  56.     {
  57.         return;
  58.     }
  59.  
  60.     ps->legsAnim = ( ( ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
  61. }
  62.  
  63. /*
  64. ===================
  65. PM_ContinueLegsAnim
  66.  
  67. Continues running the given leg animation, if its a new animation then it is started
  68. ===================
  69. */
  70. void PM_ContinueLegsAnim( playerState_t* ps, int anim ) 
  71. {
  72.     if ( ( ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) 
  73.     {
  74.         return;
  75.     }
  76.     
  77.     PM_StartLegsAnim( ps, anim );
  78. }
  79.  
  80. /*
  81. ===================
  82. PM_ForceLegsAnim
  83. ===================
  84. */
  85. void PM_ForceLegsAnim( playerState_t* ps, int anim) 
  86. {
  87.     PM_StartLegsAnim( ps, anim );
  88. }
  89.  
  90. /*
  91. ===================
  92. PM_StartTorsoAnim
  93. ===================
  94. */
  95. void PM_StartTorsoAnim( playerState_t* ps, int anim, int time ) 
  96. {
  97.     if ( anim == -1 )
  98.     {
  99.         return;
  100.     }
  101.  
  102.     if ( ps->pm_type >= PM_DEAD ) 
  103.     {
  104.         return;
  105.     }
  106.     
  107.     ps->torsoAnim  = ( ( ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
  108.     ps->torsoTimer = time;
  109. }
  110.  
  111. /*
  112. ===================
  113. PM_ContinueTorsoAnim
  114.  
  115. Continues running the given torso animation, if its a new animation then it is started
  116. ===================
  117. */
  118. static void PM_ContinueTorsoAnim( playerState_t* ps, int anim ) 
  119. {
  120.     if ( anim == -1 )
  121.     {
  122.         return;
  123.     }
  124.  
  125.     if ( ( ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) 
  126.     {
  127.         return;
  128.     }
  129.     
  130.     PM_StartTorsoAnim( ps, anim, 0 );
  131. }
  132.  
  133. /*
  134. ==============
  135. PM_TorsoAnimation
  136.  
  137. Sets the current torso animation based on the given playerstate
  138. ==============
  139. */
  140. void PM_TorsoAnimation( playerState_t* ps ) 
  141. {
  142.     switch ( ps->weaponstate ) 
  143.     {
  144.         case WEAPON_SPAWNING:
  145.         case WEAPON_READY:
  146.  
  147.             if ( ps->stats[STAT_USEWEAPONDROP] )
  148.             {
  149.                 PM_ContinueTorsoAnim ( ps, TORSO_USE );
  150.             }
  151.             else if ( (ps->pm_flags & PMF_ZOOMED) && weaponData[ps->weapon].animIdleZoomed )
  152.             {
  153.                 PM_ContinueTorsoAnim ( ps, weaponData[ps->weapon].animIdleZoomed );
  154.             }
  155.             else
  156.             {
  157.                 PM_ContinueTorsoAnim ( ps, weaponData[ps->weapon].animIdle );
  158.             }
  159.  
  160.             break;
  161.     }
  162. }
  163.  
  164. /*
  165. =================
  166. BG_ParseInventory
  167.  
  168. Parses the inventory items indicated in the given group and returns
  169. a linked list containing the results
  170. =================
  171. */
  172. TInventoryTemplate *BG_ParseInventory ( TGPGroup group )
  173. {
  174.     TInventoryTemplate    *top, *inv;
  175.     TGPGroup            subGroup;
  176.     char                temp[1024];
  177.  
  178.     // Convienience for handling no items
  179.     if (!group)
  180.     {
  181.         return 0;
  182.     }
  183.  
  184.     top = NULL;
  185.  
  186.     // Parse each of the inventory items
  187.     subGroup = trap_GPG_GetSubGroups( group );
  188.     while(subGroup)
  189.     {
  190.         trap_GPG_GetName ( subGroup, temp );
  191.  
  192.         // If the group name isnt item then 'Item' then its not an inventory item
  193.         if ( Q_stricmp ( temp, "Item") == 0)
  194.         {
  195.             inv = (TInventoryTemplate *)trap_VM_LocalAlloc ( sizeof(*inv));
  196.  
  197.             // Name of the item
  198.             trap_GPG_FindPairValue ( subGroup, "Name||Name1", "", temp );
  199.             inv->mName = trap_VM_LocalStringAlloc ( temp );
  200.  
  201.             // Bolt for the item
  202.             trap_GPG_FindPairValue ( subGroup, "Bolt", "", temp );
  203.             inv->mBolt = trap_VM_LocalStringAlloc ( temp );
  204.  
  205.             trap_GPG_FindPairValue ( subGroup, "mp_onback||onback", "no", temp );
  206.             if ( !Q_stricmp ( temp, "yes" ) )
  207.             {
  208.                 inv->mOnBack = qtrue;
  209.             }
  210.  
  211.             inv->mNext = top;
  212.             top = inv;
  213.         }
  214.  
  215.         // Move to the next group    
  216.         subGroup = trap_GPG_GetNext ( subGroup );
  217.     }
  218.     
  219.     return top;
  220. }
  221.  
  222. /*
  223. =================
  224. BG_ParseSkins
  225.  
  226. Parses the skins contained in the given group and returns a linked
  227. list with the results
  228. =================
  229. */
  230. TSkinTemplate *BG_ParseSkins( TCharacterTemplate* character, TGPGroup group )
  231. {
  232.     TSkinTemplate    *skin;
  233.     TIdentity        *identity;
  234.     TGPGroup        subGroup;
  235.     char            temp[1024];
  236.     fileHandle_t    f;
  237. #ifndef SPECIAL_PRE_CACHE
  238.     int                len;
  239. #endif
  240.     qboolean        validSkin;
  241.  
  242.     character->mSkins = NULL;
  243.  
  244.     // Parse the skin file first
  245.     trap_GPG_FindPairValue ( group, "SkinFile", "", temp );
  246.     if ( temp[0] )
  247.     {
  248.         skin = (TSkinTemplate *) trap_VM_LocalAlloc ( sizeof(*skin) );
  249.  
  250.         skin->mSkin = trap_VM_LocalStringAlloc ( temp );
  251.         skin->mNext = character->mSkins;
  252.         character->mSkins = skin;
  253.     }
  254.  
  255.     // Now parse all the skin groups
  256.     subGroup = trap_GPG_GetSubGroups ( group );
  257.     while(subGroup)
  258.     {
  259.         trap_GPG_GetName ( subGroup, temp );
  260.  
  261.         // If the groups name isnt 'Skin' then skip it
  262.         if ( Q_stricmp( temp, "Skin") == 0 )
  263.         {
  264.             // Allocate memory for the skin
  265.             skin = (TSkinTemplate *) trap_VM_LocalAlloc ( sizeof(*skin) );
  266.  
  267.             // Grab the skin filename
  268.             trap_GPG_FindPairValue ( subGroup, "File", "", temp );
  269.             skin->mSkin = trap_VM_LocalStringAlloc ( temp );
  270.  
  271. #ifdef SPECIAL_PRE_CACHE
  272.             f = 0;
  273.             validSkin = qtrue;
  274. #else
  275.             Com_sprintf( temp, sizeof(temp), "models/characters/skins/%s.g2skin", skin->mSkin );
  276.             len = trap_FS_FOpenFile( temp, &f, FS_READ );
  277.             if (f != 0) 
  278.             {
  279.                 trap_FS_FCloseFile(f);
  280.                 validSkin = qtrue;
  281.             }
  282.             else
  283.             {
  284.                 validSkin = qfalse;
  285.             }
  286. #endif
  287.  
  288.             // Parse the inventory for the skin
  289.             skin->mInventory = BG_ParseInventory( trap_GPG_FindSubGroup ( subGroup, "Inventory") );
  290.  
  291.             // Link the skin into the skin linked list
  292.             skin->mNext = character->mSkins;
  293.             character->mSkins = skin;
  294.  
  295.             // If the character isnt deathmatch then dont add it to the 
  296.             // identity list
  297.             if ( character->mDeathmatch && validSkin)
  298.             {
  299.                 // Allocate a new identity
  300.                 identity = &bg_identities[bg_identityCount++];
  301.  
  302.                 identity->mCharacter = character;
  303.                 identity->mSkin      = skin;
  304.  
  305.                 trap_GPG_FindPairValue ( subGroup, "mp_identity", "", temp );
  306.                 if ( !temp[0] )
  307.                 {
  308.                     identity->mName = trap_VM_LocalStringAlloc ( va("%s/%s", character->mName, skin->mSkin ) );
  309.                 }
  310.                 else
  311.                 {
  312.                     identity->mName = trap_VM_LocalStringAlloc ( temp );
  313.                 }
  314.  
  315.                 // Team name?
  316.                 trap_GPG_FindPairValue ( subGroup, "mp_team", "", temp );
  317.                 if ( temp[0] )
  318.                 {
  319.                     identity->mTeam = trap_VM_LocalStringAlloc ( temp );
  320.                 }
  321.                 else
  322.                 {
  323.                     identity->mTeam = "";
  324.                 }
  325.             }
  326.         }
  327.  
  328.         // Move to the next sub group in the parsers list
  329.         subGroup = trap_GPG_GetNext ( subGroup );
  330.     }
  331.     
  332.     return character->mSkins;
  333. }
  334.  
  335. /*
  336. =================
  337. BG_ParseModelSounds
  338.  
  339. Parses the model sounds for the given group and returns a linked
  340. list with the results
  341. =================
  342. */
  343. TModelSounds *BG_ParseModelSounds( TGPGroup group )
  344. {
  345.     TModelSounds    *top;
  346.     TModelSounds    *sounds;
  347.     TGPGroup        pairs;
  348.     char            temp[1024];
  349.  
  350.     // Convienience
  351.     if ( !group )
  352.     {
  353.         return NULL;
  354.     }
  355.  
  356.     top = NULL;
  357.  
  358.     // Now parse all the skin groups
  359.     pairs = trap_GPG_GetPairs ( group );
  360.     while(pairs)
  361.     {
  362.         // Allocate memory for the sounds
  363.         sounds = (TModelSounds*) trap_VM_LocalAlloc ( sizeof(*sounds) );
  364.         sounds->mNext = top;
  365.         top = sounds;
  366.  
  367.         // Grab the sounds name
  368.         trap_GPV_GetName ( pairs, temp );
  369.         sounds->mName = trap_VM_LocalStringAlloc ( temp );
  370.  
  371.         // Start with no sounds
  372.         sounds->mCount = 0;
  373.  
  374.         // Should be a list
  375.         if ( trap_GPV_IsList ( pairs ) )
  376.         {
  377.             TGPValue list;
  378.             
  379.             // Run through the list
  380.             list = trap_GPV_GetList ( pairs );
  381.             while ( list && sounds->mCount < MAX_MODEL_SOUNDS )
  382.             {
  383.                 // Add the sound to the list
  384.                 trap_GPV_GetName ( list, temp );
  385.                 sounds->mSounds[sounds->mCount++] = trap_VM_LocalStringAlloc ( temp );
  386.  
  387.                 list = trap_GPV_GetNext ( list );
  388.             }
  389.         }
  390.  
  391.         // Move to the next sound set in the parsers list
  392.         pairs = trap_GPV_GetNext ( pairs );
  393.     }
  394.  
  395.     return top;
  396. }
  397.  
  398. /*
  399. =================
  400. BG_ParseItemFile
  401.  
  402. Parses the item file.  The item file contains a list of all of the 
  403. items that can be used as inventory items for a given skin
  404. =================
  405. */
  406. qboolean BG_ParseItemFile ( void )
  407. {
  408.     TGPGroup        *baseGroup, *subGroup;
  409.     TGPValue        *pairs;
  410.     TItemTemplate    *item;
  411.     TSurfaceList    *surf;
  412.     char            temp[1024];
  413.     TGenericParser2    ItemFile;
  414.  
  415.     // Create the generic parser so the item file can be parsed
  416.     ItemFile = trap_GP_ParseFile( "ext_data/sof2.item", qtrue, qfalse );
  417.     if ( !ItemFile )
  418.     {
  419.         return qfalse;
  420.     }
  421.  
  422.     baseGroup = trap_GP_GetBaseParseGroup ( ItemFile );
  423.     subGroup = trap_GPG_GetSubGroups ( baseGroup );
  424.     while(subGroup)
  425.     {
  426.         trap_GPG_GetName ( subGroup, temp );
  427.  
  428.         if (Q_stricmp( temp, "item") == 0)
  429.         {
  430.             // Is this item used for deathmatch?
  431.             trap_GPG_FindPairValue ( subGroup, "Deathmatch", "yes", temp );
  432.             if (Q_stricmp( temp, "no") == 0)
  433.             {
  434.                 subGroup = trap_GPG_GetNext ( subGroup );
  435.                 continue;
  436.             }
  437.  
  438.             // Allocate the item template and link it up to the item list
  439.             item = (TItemTemplate *) trap_VM_LocalAlloc ( sizeof(*item) );
  440.             item->mNext   = bg_itemTemplates;
  441.             bg_itemTemplates = item;
  442.  
  443.             // Name of the item
  444.             trap_GPG_FindPairValue ( subGroup, "Name", "", temp );
  445.             item->mName = trap_VM_LocalStringAlloc ( temp );
  446.  
  447.             // Model for the item
  448.             trap_GPG_FindPairValue ( subGroup, "Model", "", temp );
  449.             if ( temp[0] )
  450.             {
  451.                 item->mModel = trap_VM_LocalStringAlloc ( temp );
  452.             }
  453.  
  454.             pairs = trap_GPG_GetPairs ( subGroup );
  455.             while(pairs)
  456.             {
  457.                 trap_GPV_GetName ( pairs, temp );
  458.  
  459.                 // Surface off?
  460.                 if ( Q_stricmpn ( temp, "offsurf", 7) == 0)
  461.                 {
  462.                     // Allocate the surface structure and link it into the list
  463.                     surf = (TSurfaceList *) trap_VM_LocalAlloc ( sizeof(*surf) );
  464.                     surf->mNext = item->mOffList;
  465.                     item->mOffList = surf;
  466.  
  467.                     // Name of the surface to turn off
  468.                     trap_GPV_GetTopValue ( pairs, temp );
  469.                     surf->mName = trap_VM_LocalStringAlloc ( temp );
  470.                 }
  471.                 // Surface on?
  472.                 else if ( Q_stricmpn( temp, "onsurf", 6) == 0)
  473.                 {
  474.                     // Allocate the surface structure and link it into the list
  475.                     surf = (TSurfaceList *) trap_VM_LocalAlloc(sizeof(*surf));
  476.                     surf->mNext = item->mOnList;
  477.                     item->mOnList = surf;
  478.  
  479.                     // Name of the surface to turn off
  480.                     trap_GPV_GetTopValue ( pairs, temp );
  481.                     surf->mName = trap_VM_LocalStringAlloc ( temp );
  482.                 }
  483.  
  484.                 // Next pairs
  485.                 pairs = trap_GPV_GetNext ( pairs );
  486.             }
  487.         }
  488.  
  489.         // Next group
  490.         subGroup = trap_GPG_GetNext ( subGroup );
  491.     }
  492.  
  493.     trap_GP_Delete(&ItemFile);
  494.  
  495.     return qtrue;
  496. }
  497.  
  498. /*
  499. =================
  500. BG_FindCharacterTemplate
  501.  
  502. Finds a character template the matches the given name or NULL if
  503. a match could not be found
  504. =================
  505. */
  506. TCharacterTemplate *BG_FindCharacterTemplate (const char *name)
  507. {
  508.     TCharacterTemplate    *current;
  509.  
  510.     // Convienience
  511.     if (!name)
  512.     {
  513.         return NULL;
  514.     }
  515.  
  516.     // Linear search through all of the parsed templates
  517.     current = bg_characterTemplates;
  518.     while(current)
  519.     {
  520.         if (Q_stricmp(name, current->mName) == 0)
  521.         {
  522.             return current;
  523.         }
  524.  
  525.         current = current->mNext;
  526.     }
  527.  
  528.     // None found
  529.     return NULL;
  530. }
  531.  
  532. /*
  533. =================
  534. BG_FindItemTemplate
  535.  
  536. Finds an item template the matches the given name or NULL if a match
  537. could not be found
  538. =================
  539. */
  540. TItemTemplate *BG_FindItemTemplate(const char *name)
  541. {
  542.     TItemTemplate        *current;
  543.  
  544.     // Convienience
  545.     if (!name)
  546.     {
  547.         return NULL;
  548.     }
  549.  
  550.     // Linear search through all of the parsed items
  551.     current = bg_itemTemplates;
  552.     while(current)
  553.     {
  554.         if (Q_stricmp(name, current->mName) == 0)
  555.         {
  556.             return current;
  557.         }
  558.  
  559.         current = current->mNext;
  560.     }
  561.  
  562.     return NULL;
  563. }
  564.  
  565. /*
  566. =================
  567. BG_LinkTemplates
  568.  
  569. Cross links the various templates 
  570. =================
  571. */
  572. static void BG_LinkTemplates(void)
  573. {
  574.     TCharacterTemplate    *current;
  575.     TInventoryTemplate    *inv;
  576.     TSkinTemplate        *skin;
  577.  
  578.     current = bg_characterTemplates;
  579.     while(current)
  580.     {
  581.         // If this template has a parent then find it and link it up as 
  582.         // its parent.  Ensure that the parent doesnt link back to itself
  583.         current->mParent = BG_FindCharacterTemplate(current->mParentName);
  584.         if (current->mParent == current)
  585.         {
  586.             current->mParent = NULL;
  587.         }
  588.         // Bring over any parent items
  589.         else if ( current->mParent )
  590.         {
  591.             // No model, bring over the parents.
  592.             if ( !current->mModel )
  593.             {
  594.                 current->mModel = current->mParent->mModel;
  595.             }
  596.         }
  597.  
  598.         // Link up all the inventory for this character
  599.         inv = current->mInventory;
  600.         while(inv)
  601.         {
  602.             inv->mItem = BG_FindItemTemplate(inv->mName);        
  603.             inv = inv->mNext;
  604.         }
  605.  
  606.         // Link up all the skins for this character
  607.         skin = current->mSkins;
  608.         while(skin)
  609.         {
  610.             // Link up all the inventory items specific to the skins
  611.             inv = skin->mInventory;
  612.             while(inv)
  613.             {
  614.                 inv->mItem = BG_FindItemTemplate(inv->mName);
  615.  
  616.                 inv = inv->mNext;
  617.             }
  618.  
  619.             skin = skin->mNext;
  620.         }
  621.  
  622.         // Move on to the next character template
  623.         current = current->mNext;
  624.     }
  625. }
  626.  
  627. /*
  628. =================
  629. BG_ParseNPCFiles
  630.  
  631. Parses all the the .npc files in the npc directory and
  632. stores their info into global lists.  
  633. =================
  634. */
  635. qboolean BG_ParseNPCFiles ( void )
  636. {
  637.     int                    i, numNPCFiles, filelen;
  638.     TGPGroup            baseGroup, subGroup;
  639.     TGenericParser2        NPCFile;
  640.     const char            *currentParent = 0;
  641.     TCharacterTemplate    *newTemplate;
  642.     char                fileName[MAX_QPATH];
  643.     char                NPCFiles[4096];
  644.     char                temp[1024];
  645.     char                *fileptr;
  646.  
  647.     // Clear the current list 
  648.     bg_characterTemplates = NULL;
  649.  
  650.     // Grab the list of NPC files
  651.     numNPCFiles = trap_FS_GetFileList("NPCs", ".npc", NPCFiles, 4096 );
  652.     if ( !numNPCFiles )
  653.     {
  654.         return qfalse;
  655.     }
  656.  
  657.     // Parse each of the NPC files
  658.     fileptr = NPCFiles;
  659.     for( i=0; i<numNPCFiles; i++, fileptr += filelen+1 )
  660.     {
  661.         // Grab the length so we can skip this file later
  662.         filelen = strlen(fileptr);
  663.  
  664.         Com_sprintf(fileName, sizeof(fileName),"NPCs/%s", fileptr );
  665.  
  666.         // Create the generic parser so the item file can be parsed
  667.         NPCFile = trap_GP_ParseFile( fileName, qtrue, qfalse );
  668.         if ( !NPCFile )
  669.         {
  670.             continue;
  671.         }
  672.  
  673.         baseGroup = trap_GP_GetBaseParseGroup ( NPCFile );
  674.         subGroup = trap_GPG_GetSubGroups ( baseGroup );
  675.  
  676.         while(subGroup)
  677.         {
  678.             trap_GPG_GetName ( subGroup, temp );
  679.  
  680.             // Look for a parent template if this is the group info
  681.             if ( Q_stricmp( temp, "GroupInfo") == 0)
  682.             {
  683.                 currentParent = NULL;
  684.  
  685.                 // Is there a parent template?
  686.                 trap_GPG_FindPairValue ( subGroup, "ParentTemplate", "", temp );
  687.                 if ( temp[0] )
  688.                 {                    
  689.                     currentParent = trap_VM_LocalStringAlloc ( temp );
  690.                 }
  691.             }
  692.             // A new character template
  693.             else if ( Q_stricmp( temp, "CharacterTemplate") == 0)
  694.             {
  695.                 // Allocate the new template and link it into the global list.
  696.                 newTemplate = (TCharacterTemplate *)trap_VM_LocalAlloc (sizeof(*newTemplate));
  697.                 newTemplate->mNext = bg_characterTemplates;
  698.                 bg_characterTemplates = newTemplate;
  699.  
  700.                 // Exclude from deathmatch?
  701.                 trap_GPG_FindPairValue ( subGroup, "DeathMatch", "yes", temp );
  702.                 if ( Q_stricmp( temp, "no") == 0)
  703.                 {
  704.                     newTemplate->mDeathmatch = qfalse;
  705.                 }
  706.                 else
  707.                 {
  708.                     newTemplate->mDeathmatch = qtrue;
  709.                 }
  710.  
  711.                 // Template name
  712.                 trap_GPG_FindPairValue ( subGroup, "Name", "", temp );
  713.                 if ( temp[0] )
  714.                 {
  715.                     newTemplate->mName = trap_VM_LocalStringAlloc ( temp );
  716.                 }
  717.  
  718.                 // Template formal name
  719.                 trap_GPG_FindPairValue ( subGroup, "FormalName", "", temp );
  720.                 if ( temp[0] )
  721.                 {
  722.                     newTemplate->mFormalName = trap_VM_LocalStringAlloc ( temp );
  723.                 }
  724.  
  725.                 // Template model 
  726.                 trap_GPG_FindPairValue ( subGroup, "Model", "", temp );
  727.                 if ( temp[0] )
  728.                 {
  729.                     newTemplate->mModel = trap_VM_LocalStringAlloc ( temp );
  730.                 }
  731.  
  732.                 // Use the current parent
  733.                 newTemplate->mParentName = currentParent;
  734.  
  735.                 // Parse inventory for this character template
  736.                 newTemplate->mInventory = BG_ParseInventory( trap_GPG_FindSubGroup( subGroup, "Inventory" ));
  737.  
  738.                 // Parse the skins for this character template
  739.                 BG_ParseSkins( newTemplate, subGroup);
  740.  
  741.                 // Parse the sounds for this character template
  742.                 newTemplate->mSounds = BG_ParseModelSounds ( trap_GPG_FindSubGroup ( subGroup, "MPSounds" ) );
  743.             }
  744.  
  745.             // Move to the next group
  746.             subGroup = trap_GPG_GetNext ( subGroup );
  747.         }
  748.  
  749.         trap_GP_Delete(&NPCFile);
  750.     }
  751.  
  752.     // Parse the item file
  753.     BG_ParseItemFile();
  754.  
  755.     // Link up all the templates
  756.     BG_LinkTemplates();
  757.     
  758.     return qtrue;
  759. }
  760.  
  761. /*
  762. =================
  763. BG_GetModelSoundsGroup
  764.  
  765. Returns the group of sounds for the given model and sound group combination, if the
  766. sound group couldnt be found NULL is returned
  767. =================
  768. */
  769. TModelSounds* BG_GetModelSoundsGroup ( const char* Identity, const char* SoundGroup )
  770. {
  771.     TIdentity            *identity;
  772.     TCharacterTemplate    *character;
  773.     TModelSounds        *sounds;
  774.     
  775.     // Grab the identity in question
  776.     identity = BG_FindIdentity (Identity );
  777.     if ( !identity )
  778.     {
  779.         return NULL;
  780.     }
  781.  
  782.     character = identity->mCharacter;
  783.  
  784.     while ( character )
  785.     {
  786.         // Run through the sounds and look for the match
  787.         sounds = character->mSounds;
  788.         while ( sounds )
  789.         {
  790.             // Match?
  791.             if ( !Q_stricmp ( sounds->mName, SoundGroup ) )
  792.             {
  793.                 return sounds;
  794.             }
  795.  
  796.             sounds = sounds->mNext;
  797.         }
  798.  
  799.         character = character->mParent;
  800.     }
  801.  
  802.     // Not found
  803.     return NULL;
  804. }
  805.  
  806. /*
  807. =================
  808. BG_GetModelSoundCount
  809.  
  810. Return the number of sounds for the given model
  811. =================
  812. */
  813. int BG_GetModelSoundCount ( const char *Identity, const char *SoundGroup )
  814. {
  815.     TModelSounds* sounds;
  816.  
  817.     // Grab the sounds
  818.     sounds = BG_GetModelSoundsGroup( Identity, SoundGroup );
  819.     if ( !sounds )
  820.     {
  821.         return 0;
  822.     }
  823.  
  824.     // Return the sound count
  825.     return sounds->mCount;
  826. }
  827.  
  828. /*
  829. =================
  830. BG_GetModelSound
  831.  
  832. Returns the model sound for the given sound group and index.  If the sound
  833. could not be found then NULL is returned.
  834. =================
  835. */
  836. const char *BG_GetModelSound ( const char *Identity, const char *SoundGroup, int index )
  837. {
  838.     TModelSounds    *sounds;
  839.  
  840.     // Grab the sounds
  841.     sounds = BG_GetModelSoundsGroup( Identity, SoundGroup );
  842.     if ( !sounds )
  843.     {        
  844.         return NULL;
  845.     }
  846.  
  847.     // Invalid index?
  848.     if ( index >= sounds->mCount )
  849.     {
  850.         return "";
  851.     }
  852.  
  853.     // Run through the sounds and look for the match
  854.     return sounds->mSounds[index];
  855. }
  856.  
  857. /*
  858. =================
  859. BG_FindIdentity
  860.  
  861. Returns the identity with the given name
  862. =================
  863. */
  864. TIdentity *BG_FindIdentity ( const char *identityName )
  865. {
  866.     int i;
  867.  
  868.     // Convienience
  869.     if (!identityName)
  870.     {
  871.         return NULL;
  872.     }
  873.  
  874.     // Linear search through all of the parsed identities
  875.     for ( i = 0; i < bg_identityCount; i ++ )
  876.     {
  877.         if (Q_stricmp(identityName, bg_identities[i].mName) == 0)
  878.         {
  879.             return &bg_identities[i];
  880.         }
  881.     }
  882.  
  883.     // None found
  884.     return NULL;
  885. }
  886.  
  887. /*
  888. =================
  889. BG_FindTeamIdentity
  890.  
  891. returns the first identity which matches the given team name
  892. =================
  893. */
  894. #define MAX_TEAM_IDENTS    5
  895. TIdentity* BG_FindTeamIdentity ( const char* teamName, int index )
  896. {
  897.     TIdentity* idents[MAX_TEAM_IDENTS];
  898.     int           count;
  899.     int           i;
  900.  
  901.     // Convienience
  902.     if ( !teamName )
  903.     {
  904.         return NULL;
  905.     }
  906.  
  907.     // Linear search through all of the parsed identities
  908.     for ( i = 0, count = 0; i < bg_identityCount && count < MAX_TEAM_IDENTS; i ++ )
  909.     {
  910.         if (Q_stricmp(teamName, bg_identities[i].mTeam) == 0)
  911.         {
  912.             idents[count++] = &bg_identities[i];
  913.         }
  914.     }
  915.  
  916.     if ( !count )
  917.     {
  918.         return NULL;
  919.     }
  920.     
  921.     if ( index != -1 )
  922.     {
  923.         if ( index >= count )
  924.         {
  925.             index = count - 1;
  926.         }
  927.  
  928.         return idents[index];
  929.     }
  930.  
  931.     // None found
  932.     return idents[rand()%count];
  933. }
  934.  
  935.  
  936. /*
  937. =================
  938. BG_ParseSkin
  939.  
  940. Reads a skin file into a null terminated list and returns the number found
  941. =================
  942. */
  943. int BG_ParseSkin ( const char* filename, char* pairs, int pairsSize )
  944. {
  945.     TGenericParser2 skinFile;
  946.     TGPGroup        *basegroup;
  947.     TGPGroup        *group;
  948.     TGPGroup        *sub;
  949.     char            name[MAX_QPATH];
  950.     char*            end;
  951.     int                numPairs;
  952.  
  953.     // Open the skin file
  954.     skinFile = trap_GP_ParseFile( (char*)filename, qtrue, qfalse );
  955.     if ( !skinFile )
  956.     {
  957.         return 0;
  958.     }
  959.  
  960.     numPairs  = 0;
  961.     end          = pairs;
  962.     *end      = 0;
  963.     basegroup = trap_GP_GetBaseParseGroup ( skinFile );
  964.     group      = trap_GPG_GetSubGroups ( basegroup );
  965.  
  966.     while(group)
  967.     {
  968.         trap_GPG_GetName ( group, name );
  969.  
  970.         // Parse the material
  971.         if ( Q_stricmp ( name, "material") == 0)
  972.         {
  973.             char    matName[MAX_QPATH];
  974.             char    shaderName[MAX_QPATH];
  975.  
  976.             trap_GPG_FindPairValue ( group, "name", "", matName );
  977.  
  978.             sub = trap_GPG_FindSubGroup ( group, "group");
  979.             if (sub)
  980.             {
  981.                 trap_GPG_FindPairValue ( sub, "shader1", "", shaderName );
  982.                 if (!shaderName[0])
  983.                 {
  984.                     trap_GPG_FindPairValue ( sub, "texture1", "", shaderName );
  985.                 }
  986.             }
  987.  
  988.             if (matName[0] && shaderName[0])
  989.             {
  990.                 int size;
  991.  
  992.                 size = Com_sprintf(end, pairsSize, "%s %s ", matName, shaderName);
  993.                 end += size;
  994.                 pairsSize -= size;
  995.                 numPairs++;
  996.             }
  997.         }
  998.  
  999.         group = trap_GPG_GetNext ( group );
  1000.     }
  1001.  
  1002.     trap_GP_Delete(&skinFile);
  1003.  
  1004.     return numPairs;
  1005. }
  1006.  
  1007. /*
  1008. ==================
  1009. BG_SwingAngles
  1010. ==================
  1011. */
  1012. static void BG_SwingAngles ( 
  1013.     float        destination, 
  1014.     float        swingTolerance, 
  1015.     float        clampTolerance,
  1016.     float        speed, 
  1017.     float        *angle, 
  1018.     qboolean    *swinging, 
  1019.     int            frameTime 
  1020.     ) 
  1021. {
  1022.     float    swing;
  1023.     float    move;
  1024.     float    scale;
  1025.  
  1026.     if ( !*swinging ) 
  1027.     {
  1028.         // see if a swing should be started
  1029.         swing = AngleSubtract( *angle, destination );
  1030.         if ( swing > swingTolerance || swing < -swingTolerance ) 
  1031.         {
  1032.             *swinging = qtrue;
  1033.         }
  1034.     }
  1035.  
  1036.     if ( !*swinging ) 
  1037.     {
  1038.         return;
  1039.     }
  1040.     
  1041.     // modify the speed depending on the delta
  1042.     // so it doesn't seem so linear
  1043.     swing = AngleSubtract( destination, *angle );
  1044.     scale = fabs( swing );
  1045.     if ( scale < swingTolerance * 0.5 ) 
  1046.     {
  1047.         scale = 0.5;
  1048.     } 
  1049.     else if ( scale < swingTolerance ) 
  1050.     {
  1051.         scale = 1.0;
  1052.     } 
  1053.     else 
  1054.     {
  1055.         scale = 2.0;
  1056.     }
  1057.  
  1058.     // swing towards the destination angle
  1059.     if ( swing >= 0 ) 
  1060.     {
  1061.         move = frameTime * scale * speed;
  1062.         if ( move >= swing ) 
  1063.         {
  1064.             move = swing;
  1065.             *swinging = qfalse;
  1066.         }
  1067.         *angle = AngleMod( *angle + move );
  1068.     } 
  1069.     else if ( swing < 0 ) 
  1070.     {
  1071.         move = frameTime * scale * -speed;
  1072.         if ( move <= swing ) 
  1073.         {
  1074.             move = swing;
  1075.             *swinging = qfalse;
  1076.         }
  1077.         *angle = AngleMod( *angle + move );
  1078.     }
  1079.  
  1080.     // clamp to no more than tolerance
  1081.     swing = AngleSubtract( destination, *angle );
  1082.     if ( swing > clampTolerance ) 
  1083.     {
  1084.         *angle = AngleMod( destination - (clampTolerance - 1) );
  1085.     } 
  1086.     else if ( swing < -clampTolerance ) 
  1087.     {
  1088.         *angle = AngleMod( destination + (clampTolerance - 1) );
  1089.     }
  1090. }
  1091.  
  1092. /*
  1093. =================
  1094. CG_AddPainTwitch
  1095. =================
  1096. */
  1097. #define    PAIN_TWITCH_TIME    200
  1098. static void BG_AddPainTwitch( int painTime, int painDirection, int currentTime,  vec3_t torsoAngles ) {
  1099.     int        t;
  1100.     float    f;
  1101.  
  1102.     t = currentTime - painTime;
  1103.     if ( t >= PAIN_TWITCH_TIME ) {
  1104.         return;
  1105.     }
  1106.  
  1107.     f = 1.0 - (float)t / PAIN_TWITCH_TIME;
  1108.  
  1109.     if ( painDirection ) {
  1110.         torsoAngles[ROLL] += 20 * f;
  1111.     } else {
  1112.         torsoAngles[ROLL] -= 20 * f;
  1113.     }
  1114. }
  1115.  
  1116. /*
  1117. =================
  1118. BG_CalculateLeanOffset
  1119. =================
  1120. */
  1121. float BG_CalculateLeanOffset ( int leanTime )
  1122. {
  1123.     return ((float)(leanTime - LEAN_TIME) / LEAN_TIME * LEAN_OFFSET);
  1124. }
  1125.  
  1126. /*
  1127. =================
  1128. BG_PlayerAngles
  1129. =================
  1130. */
  1131. void BG_PlayerAngles ( 
  1132.  
  1133.     vec3_t        startAngles, 
  1134.     vec3_t        legs[3], 
  1135.  
  1136.     vec3_t        legsAngles,                // out
  1137.     vec3_t        lowerTorsoAngles,
  1138.     vec3_t        upperTorsoAngles,
  1139.     vec3_t        headAngles,
  1140.  
  1141.     int            leanOffset,
  1142.  
  1143.     int            painTime, 
  1144.     int            painDirection, 
  1145.     int            currentTime,
  1146.  
  1147.     animInfo_t*    torsoInfo,
  1148.     animInfo_t*    legsInfo,
  1149.  
  1150.     int            frameTime, 
  1151.     vec3_t        realvelocity,
  1152.     qboolean    dead,
  1153.     float        movementDir,
  1154.     void*        ghoul2
  1155.     )
  1156. {
  1157.     float        dest;
  1158.     static    int    movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 };
  1159.     float        speed;
  1160.     int            dir;
  1161.     vec3_t        velocity;
  1162.  
  1163.     VectorCopy( startAngles, headAngles );
  1164.     VectorCopy ( realvelocity, velocity );
  1165.     headAngles[YAW] = AngleMod( headAngles[YAW] );
  1166.     VectorClear( legsAngles );
  1167.     VectorClear( lowerTorsoAngles );    
  1168.     VectorClear( upperTorsoAngles );
  1169.  
  1170.     // --------- yaw -------------
  1171.  
  1172.     // allow yaw to drift a bit
  1173.     if ( ( legsInfo->anim & ~ANIM_TOGGLEBIT ) != TORSO_IDLE_PISTOL  ) 
  1174.     {
  1175.         // if not standing still, always point all in the same direction
  1176.         torsoInfo->yawing   = qtrue;
  1177.         torsoInfo->pitching = qtrue;
  1178.         legsInfo->yawing    = qtrue;
  1179.     }
  1180.  
  1181.     speed = VectorNormalize( velocity );
  1182.  
  1183.     // adjust legs for movement dir
  1184.     if (dead) 
  1185.     {
  1186.         dir = 0;
  1187.     } 
  1188.     else 
  1189.     {
  1190.         dir = movementDir;
  1191.  
  1192.         if ( leanOffset && !speed )
  1193.         {
  1194.             dir = 0;
  1195.         }
  1196.     }
  1197.     
  1198. //    legsAngles[YAW]   = headAngles[YAW] + 2 * movementOffsets[ dir ];
  1199. //    torsoAngles[YAW]  = headAngles[YAW] + 2 * movementOffsets[ dir ];
  1200.     legsAngles[YAW]   = headAngles[YAW] + 2 * movementOffsets[ dir ];
  1201.     lowerTorsoAngles[YAW]  = headAngles[YAW] + 2 * movementOffsets[ dir ];
  1202.  
  1203.     // torso
  1204.     BG_SwingAngles( lowerTorsoAngles[YAW], 25, 90, 0.3f, &torsoInfo->yawAngle, &torsoInfo->yawing, frameTime );
  1205.     BG_SwingAngles( legsAngles[YAW],  40, 90, 0.3f, &legsInfo->yawAngle,  &legsInfo->yawing,  frameTime );
  1206.  
  1207.     if ( leanOffset )
  1208.     {
  1209.         legsAngles[YAW] = headAngles[YAW];
  1210.     }
  1211.     else
  1212.     {
  1213.         legsAngles[YAW] = legsInfo->yawAngle;
  1214.     }
  1215.  
  1216.     lowerTorsoAngles[YAW] = Com_Clampf ( -90, 90, AngleDelta (headAngles[YAW], legsAngles[YAW] ) );
  1217.     upperTorsoAngles[YAW] = lowerTorsoAngles[YAW] / 2;
  1218.     lowerTorsoAngles[YAW]  = legsAngles[YAW] + lowerTorsoAngles[YAW] / 2;
  1219.  
  1220.     headAngles[YAW] -= upperTorsoAngles[YAW];
  1221.  
  1222.  
  1223.     // --------- pitch -------------
  1224.  
  1225.     // only show a fraction of the pitch angle in the torso
  1226.     if ( headAngles[PITCH] > 180 ) {
  1227.         dest = (-360 + headAngles[PITCH]) * 0.75;
  1228.     } else {
  1229.         dest = headAngles[PITCH] * 0.75;
  1230.     }
  1231.  
  1232.     lowerTorsoAngles[PITCH] = dest;
  1233.  
  1234.     // --------- roll -------------
  1235.  
  1236.     // lean towards the direction of travel
  1237.  
  1238.     // Add in leanoffset
  1239.     if ( leanOffset )
  1240.     {
  1241.         lowerTorsoAngles[ROLL] -= ((float)leanOffset * 1.25f);
  1242.         lowerTorsoAngles[YAW] -= 1.25f * ((float)leanOffset/LEAN_OFFSET) * dest;
  1243.         headAngles[YAW] -= ((float)leanOffset/LEAN_OFFSET) * dest;
  1244.         headAngles[ROLL] -= ((float)leanOffset * 1.25f);
  1245.     }
  1246.     else if ( speed ) 
  1247.     {
  1248.         vec3_t    axis[3];
  1249.         float    side;
  1250.  
  1251.         speed *= 0.025;
  1252.  
  1253.         AnglesToAxis( legsAngles, axis );
  1254.         side = speed * DotProduct( velocity, axis[1] );
  1255.         legsAngles[ROLL] -= side;
  1256.     }
  1257.  
  1258.     // pain twitch
  1259.     BG_AddPainTwitch( painTime, painDirection, currentTime, lowerTorsoAngles );
  1260.  
  1261.     // pull the angles back out of the hierarchial chain
  1262.     AnglesSubtract( headAngles, lowerTorsoAngles, headAngles );
  1263.     AnglesSubtract( lowerTorsoAngles, legsAngles, lowerTorsoAngles );
  1264.  
  1265.     if ( legs && ghoul2 )
  1266.     {
  1267.         AnglesToAxis( legsAngles, legs );
  1268.  
  1269.         // Apply the rotations
  1270.         trap_G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", upperTorsoAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, currentTime); 
  1271.  
  1272.         trap_G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", lowerTorsoAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, currentTime); 
  1273.         trap_G2API_SetBoneAngles(ghoul2, 0, "cranium", headAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, 0,0, currentTime); 
  1274.     }
  1275. }
  1276.  
  1277. /*
  1278. ======================
  1279. BG_ParseAnimationFile
  1280.  
  1281. Read a configuration file containing animation counts and rates
  1282. models/players/visor/animation.cfg, etc
  1283. ======================
  1284. */
  1285. qboolean BG_ParseAnimationFile ( const char *filename, animation_t* animations ) 
  1286. {
  1287.     const char        *text_p;
  1288.     int                len;
  1289.     int                i;
  1290.     char            *token;
  1291.     float            fps;
  1292.     int                skip;
  1293.     char            text[20000];
  1294.     fileHandle_t    f;
  1295.     int                animNum;
  1296.  
  1297.     // load the file
  1298.     len = trap_FS_FOpenFile( filename, &f, FS_READ );
  1299.     if ( len <= 0 || len >= sizeof( text ) - 1 ) 
  1300.     {
  1301.         return qfalse;
  1302.     }
  1303.  
  1304.     trap_FS_Read( text, len, f );
  1305.     trap_FS_FCloseFile( f );
  1306.  
  1307.     // parse the text
  1308.     text[len] = 0;
  1309.     text_p    = text;
  1310.     skip      = 0;
  1311.  
  1312.     //initialize anim array so that from 0 to MAX_ANIMATIONS, set default values of 0 1 0 100
  1313.     for(i = 0; i < MAX_ANIMATIONS; i++)
  1314.     {
  1315.         animations[i].firstFrame = 0;
  1316.         animations[i].numFrames = 0;
  1317.         animations[i].loopFrames = -1;
  1318.         animations[i].frameLerp = 100;
  1319.         animations[i].initialLerp = 100;
  1320.     }
  1321.  
  1322.     // read information for each frame
  1323.     while(1) 
  1324.     {
  1325.         token = COM_Parse( &text_p );
  1326.  
  1327.         if ( !token || !token[0]) 
  1328.         {
  1329.             break;
  1330.         }
  1331.  
  1332.         animNum = GetIDForString(bg_animTable, token);
  1333.         if(animNum == -1)
  1334.         {
  1335. //#ifndef FINAL_BUILD
  1336. #ifdef _DEBUG
  1337.             Com_Printf(S_COLOR_RED"WARNING: Unknown token %s in %s\n", token, filename);
  1338. #endif
  1339.             continue;
  1340.         }
  1341.  
  1342.         token = COM_Parse( &text_p );
  1343.         if ( !token ) 
  1344.         {
  1345.             break;
  1346.         }
  1347.         animations[animNum].firstFrame = atoi( token );
  1348.  
  1349.         token = COM_Parse( &text_p );
  1350.         if ( !token ) 
  1351.         {
  1352.             break;
  1353.         }
  1354.         animations[animNum].numFrames = atoi( token );
  1355.  
  1356.         token = COM_Parse( &text_p );
  1357.         if ( !token ) 
  1358.         {
  1359.             break;
  1360.         }
  1361.         animations[animNum].loopFrames = atoi( token );
  1362.  
  1363.         token = COM_Parse( &text_p );
  1364.         if ( !token ) 
  1365.         {
  1366.             break;
  1367.         }
  1368.         fps = atof( token );
  1369.         if ( fps == 0 ) 
  1370.         {
  1371.             fps = 1;//Don't allow divide by zero error
  1372.         }
  1373.         if ( fps < 0 )
  1374.         {//backwards
  1375.             animations[animNum].frameLerp = floor(1000.0f / fps);
  1376.         }
  1377.         else
  1378.         {
  1379.             animations[animNum].frameLerp = ceil(1000.0f / fps);
  1380.         }
  1381.  
  1382.         animations[animNum].initialLerp = ceil(1000.0f / fabs(fps));
  1383.     }
  1384.  
  1385.     return qtrue;
  1386. }
  1387.  
  1388. /*
  1389. ========================
  1390. BG_SetAvailableOutfitting
  1391.  
  1392. Set the current availability table
  1393. ========================
  1394. */
  1395. void BG_SetAvailableOutfitting ( const char* available )
  1396. {
  1397.     int        len;
  1398.  
  1399.     len = strlen ( available );
  1400.     if ( len > WP_NUM_WEAPONS )
  1401.     {
  1402.         len = WP_NUM_WEAPONS;
  1403.     }
  1404.  
  1405.     // IF the availability has changed force a reload of the outfitting groups
  1406.     if ( Q_strncmp ( available, bg_availableOutfitting, min(sizeof(bg_availableOutfitting),len) ) )
  1407.     {
  1408.         bg_outfittingCount = 0;
  1409.     }
  1410.  
  1411.     // Initialize it to all on.
  1412.     memset ( &bg_availableOutfitting[0], '2', sizeof(bg_availableOutfitting) );
  1413.     memcpy ( &bg_availableOutfitting[0], available, len );
  1414. }
  1415.  
  1416. /*
  1417. ========================
  1418. BG_IsWeaponAvailableForOutfitting
  1419.  
  1420. Is the given weapon available for outfitting?
  1421. ========================
  1422. */
  1423. qboolean BG_IsWeaponAvailableForOutfitting ( weapon_t weapon, int level )
  1424. {
  1425.     if ( bg_availableOutfitting[0] == -1 )
  1426.     {
  1427.         return qtrue;
  1428.     }
  1429.     
  1430.     if ( bg_availableOutfitting[weapon-1] - '0' >= level )
  1431.     {
  1432.         return qtrue;
  1433.     }
  1434.  
  1435.     return qfalse;
  1436. }
  1437.  
  1438. /*
  1439. ========================
  1440. BG_DecompressOutfitting
  1441.  
  1442. Decompresses the given outfitting string into the outfitting structure
  1443. ========================
  1444. */
  1445. void BG_DecompressOutfitting ( const char* compressed, goutfitting_t* outfitting)
  1446. {
  1447.     int    group;
  1448.     int origitem;
  1449.  
  1450.     memset ( outfitting->items, 0, sizeof(outfitting->items) );
  1451.  
  1452.     for ( group = 0; group < OUTFITTING_GROUP_MAX; group ++ )
  1453.     {
  1454.         int item;
  1455.  
  1456.         if ( !*compressed )
  1457.         {
  1458.             item = -1;
  1459.         }
  1460.         else
  1461.         {
  1462.             item = ((*compressed++) - 'A');
  1463.         }
  1464.  
  1465.         // Valid item number?
  1466.         if ( item < 0 || item >= 10 )
  1467.         {
  1468.             item = 0;
  1469.         }
  1470.  
  1471.         // Valid slot for the group ?
  1472.         if ( bg_outfittingGroups[group][item] == -1 )
  1473.         {
  1474.             continue;
  1475.         }            
  1476.  
  1477.         // Ok to set the item now    
  1478.         outfitting->items[group] = item;
  1479.                             
  1480.         // No initialized
  1481.         if ( bg_availableOutfitting[0] == -1 )
  1482.         {
  1483.             continue;
  1484.         }
  1485.  
  1486.         // Is it available?
  1487.         if ( bg_itemlist[bg_outfittingGroups[group][item]].giType == IT_WEAPON )
  1488.         {
  1489.             origitem = item;
  1490.             while ( !BG_IsWeaponAvailableForOutfitting ( bg_itemlist[bg_outfittingGroups[group][item]].giTag, 2 ) )
  1491.             {
  1492.                 item++;
  1493.                 if ( bg_outfittingGroups[group][item] == -1 )
  1494.                 {
  1495.                     item = 0;
  1496.                 }
  1497.  
  1498.                 if ( item == origitem )
  1499.                 {
  1500.                     //Com_Error ( ERR_FATAL, "ERROR: There must be at least one weapon available in each category" );
  1501.                     item = -1;
  1502.                     break;
  1503.                 }
  1504.             }
  1505.         }
  1506.  
  1507.         // Ok to set the item now    
  1508.         outfitting->items[group] = item;
  1509.     }
  1510. }
  1511.  
  1512. /*
  1513. ========================
  1514. BG_CompressOutfitting
  1515.  
  1516. Compresses the given outfitting structure into the given string
  1517. ========================
  1518. */
  1519. void BG_CompressOutfitting ( goutfitting_t* outfitting, char* compressed, int size )
  1520. {
  1521.     int i;
  1522.  
  1523.     for ( i = 0; i < OUTFITTING_GROUP_MAX && size; i ++, size-- )
  1524.     {
  1525.         *compressed++ = outfitting->items[i] + 'A';
  1526.     }
  1527.  
  1528.     *compressed = '\0';
  1529. }
  1530.  
  1531. /*
  1532. ========================
  1533. BG_FindOutfitting
  1534.  
  1535. Finds the real outfitting that matches the given outfitting
  1536. ========================
  1537. */
  1538. int BG_FindOutfitting ( goutfitting_t* outfitting )
  1539. {
  1540.     int i;
  1541.     int l;
  1542.  
  1543.     // Loop through all the outfittings linearly
  1544.     for ( i = 0; i < bg_outfittingCount; i ++ )
  1545.     {
  1546.         for ( l = 0; l < OUTFITTING_GROUP_MAX; l ++ )
  1547.         {
  1548.             if ( bg_outfittings[i].items[l] != outfitting->items[l] )
  1549.             {
  1550.                 break;
  1551.             }
  1552.         }
  1553.  
  1554.         // Both iterators at the end signifies a match
  1555.         if ( l == OUTFITTING_GROUP_MAX )
  1556.         {
  1557.             return i;
  1558.         }
  1559.     }
  1560.  
  1561.     return -1;
  1562. }
  1563.  
  1564. /*
  1565. ========================
  1566. BG_ParseOutfittingTemplate
  1567.  
  1568. Parses a single outfitting template
  1569. ========================
  1570. */
  1571. qboolean BG_ParseOutfittingTemplate ( const char* fileName, goutfitting_t* outfitting )
  1572. {
  1573.     TGPGroup            baseGroup;
  1574.     TGPGroup            subGroup;
  1575.     TGenericParser2        file;
  1576.     TGPGroup            pairs;
  1577.     char                temp[MAX_OUTFITTING_NAME];
  1578.  
  1579.     // Initialize the outfitting
  1580.     memset ( outfitting, 0, sizeof(goutfitting_t) );
  1581.  
  1582.     // Create the generic parser so the item file can be parsed
  1583.     file = trap_GP_ParseFile( (char*)fileName, qtrue, qfalse );
  1584.     if ( !file )
  1585.     {
  1586.         return qfalse;
  1587.     }
  1588.  
  1589.     // Start at the top with the "outfitting" group
  1590.     baseGroup = trap_GP_GetBaseParseGroup ( file );
  1591.     subGroup  = trap_GPG_FindSubGroup ( baseGroup, "outfitting" );
  1592.     if ( !subGroup )
  1593.     {
  1594.         trap_GP_Delete(&file);
  1595.         return qfalse;
  1596.     }
  1597.  
  1598.     // Get the name of the template
  1599.     trap_GPG_FindPairValue ( subGroup, "displayName", fileName, outfitting->name );
  1600.  
  1601.     // Sub group named "items"
  1602.     pairs = trap_GPG_FindPair ( subGroup, "items" );
  1603.     if ( pairs )
  1604.     {
  1605.         TGPValue list;
  1606.         
  1607.         // Run through the list
  1608.         list = trap_GPV_GetList ( pairs );
  1609.         while ( list )
  1610.         {
  1611.             gitem_t*    item;
  1612.  
  1613.             trap_GPV_GetName ( list, temp );
  1614.  
  1615.             item = BG_FindItem ( temp );
  1616.             if ( item )
  1617.             {
  1618.                 int index;
  1619.                 for ( index=0; bg_outfittingGroups[item->outfittingGroup][index] != -1; index ++ )
  1620.                 {
  1621.                     if ( bg_outfittingGroups[item->outfittingGroup][index] == (item - &bg_itemlist[0]) )
  1622.                     {
  1623.                         outfitting->items[item->outfittingGroup] = index;
  1624.                         break;
  1625.                     }
  1626.                 }
  1627.             }
  1628.  
  1629.             list = trap_GPV_GetNext ( list );
  1630.         }
  1631.     }
  1632.  
  1633.     // Sub group named "weapons"
  1634.     pairs = trap_GPG_FindPair ( subGroup, "weapons" );
  1635.  
  1636.     // Run through the weapons
  1637.     if ( pairs )
  1638.     {
  1639.         TGPValue list;
  1640.         
  1641.         // Run through the list
  1642.         list = trap_GPV_GetList ( pairs );
  1643.         while ( list )
  1644.         {
  1645.             gitem_t*    item;
  1646.             int            i;
  1647.  
  1648.             trap_GPV_GetName ( list, temp );
  1649.  
  1650.             // Lookup the weapon number
  1651.             for( i = WP_NONE + 1, item=NULL; i < WP_NUM_WEAPONS; i++ )
  1652.             {
  1653.                 if ( Q_stricmp(bg_weaponNames[i], temp ) == 0)
  1654.                 {                    
  1655.                     // translate the weapon index into an item index.
  1656.                     item = BG_FindWeaponItem ( (weapon_t) i );                    
  1657.                     break;
  1658.                 }
  1659.             }            
  1660.  
  1661.             // If the weapon translated into an item ok then drop it
  1662.             // in its appropriate slot.
  1663.             if ( item )
  1664.             {
  1665.                 int index;
  1666.     
  1667.                 // Make sure outfitting groups that have weapons that are not available
  1668.                 // do not show up
  1669.                 if ( !BG_IsWeaponAvailableForOutfitting ( item->giTag, 2 ) )
  1670.                 {
  1671.                     trap_GP_Delete(&file);
  1672.                     return qfalse;
  1673.                 }
  1674.  
  1675.                 for ( index=0; bg_outfittingGroups[item->outfittingGroup][index] != -1; index ++ )
  1676.                 {
  1677.                     if ( bg_outfittingGroups[item->outfittingGroup][index] == (item - &bg_itemlist[0]) )
  1678.                     {
  1679.                         outfitting->items[item->outfittingGroup] = index;
  1680.                         break;
  1681.                     }
  1682.                 }
  1683.             }
  1684.  
  1685.             list = trap_GPV_GetNext ( list );
  1686.         }
  1687.     }
  1688.  
  1689.     trap_GP_Delete(&file);
  1690.  
  1691.     return qtrue;
  1692. }
  1693.  
  1694. /*
  1695. ========================
  1696. BG_ParseOutfittingTemplates
  1697.  
  1698. Parses the available outfitting templates
  1699. ========================
  1700. */
  1701. int BG_ParseOutfittingTemplates ( qboolean force )
  1702. {
  1703.     int        i;
  1704.     int        numOutfittingFiles;
  1705.     int        filelen;
  1706.     char    fileName[MAX_QPATH];
  1707.     char    outfittingFiles[4096];
  1708.     char    *fileptr;
  1709.  
  1710.     // Dont reload unless forced
  1711.     if ( bg_outfittingCount && !force )
  1712.     {
  1713.         return bg_outfittingCount;
  1714.     }
  1715.  
  1716.     // Clear the current list 
  1717.     bg_outfittingCount = 1;
  1718.     strcpy ( bg_outfittings[0].name, "CUSTOM" );
  1719.  
  1720.     // Grab the list of NPC files
  1721.     numOutfittingFiles = trap_FS_GetFileList("scripts", ".outfitting", outfittingFiles, 4096 );
  1722.     if ( !numOutfittingFiles )
  1723.     {
  1724.         return 0;
  1725.     }
  1726.  
  1727.     // Parse each of the NPC files
  1728.     fileptr = outfittingFiles;
  1729.     for( i = 0; i < numOutfittingFiles; i++, fileptr += filelen+1 )
  1730.     {
  1731.         // Grab the length so we can skip this file later
  1732.         filelen = strlen(fileptr);
  1733.  
  1734.         Com_sprintf ( fileName, sizeof(fileName), "scripts/%s", fileptr );
  1735.  
  1736.         // Parse the outfitting template
  1737.         if ( BG_ParseOutfittingTemplate ( fileName, &bg_outfittings[bg_outfittingCount]    ) )
  1738.         {
  1739.             bg_outfittingCount++;
  1740.         }
  1741.     }
  1742.  
  1743.     return bg_outfittingCount;
  1744. }
  1745.  
  1746.  
  1747.